#define VMTEST 6

#include "types.h"
#include "globals.h"
#include "kernel.h"

#include "util/gdb.h"
#include "util/init.h"
#include "util/debug.h"
#include "util/string.h"
#include "util/printf.h"

#include "mm/mm.h"
#include "mm/page.h"
#include "mm/pagetable.h"
#include "mm/pframe.h"
#include "mm/mman.h"

#include "vm/vmmap.h"
#include "vm/shadow.h"
#include "vm/anon.h"

#include "main/acpi.h"
#include "main/apic.h"
#include "main/interrupt.h"
#include "main/cpuid.h"
#include "main/gdt.h"

#include "proc/sched.h"
#include "proc/proc.h"
#include "proc/kthread.h"

#include "drivers/dev.h"
#include "drivers/blockdev.h"
#include "drivers/tty/virtterm.h"

#include "api/exec.h"
#include "api/syscall.h"

#include "fs/vfs.h"
#include "fs/vnode.h"
#include "fs/file.h"
#include "fs/vfs_syscall.h"
#include "fs/fcntl.h"
#include "fs/stat.h"
#include "fs/file.h"
#include "errno.h"
#include "limits.h"

#include "test/kshell/kshell.h"
#include "test/kshell/io.h"

GDB_DEFINE_HOOK(boot)
GDB_DEFINE_HOOK(initialized)
GDB_DEFINE_HOOK(shutdown)

static void *bootstrap(int arg1, void *arg2);
static void *idleproc_run(int arg1, void *arg2);
static kthread_t *initproc_create(void);
static void *initproc_run(int arg1, void *arg2);
static void hard_shutdown(void);

/*initproc_run() test case*/
static void do_shell();
static void* kshell_invoke(int arg1, void *arg2);
int do_test_runkill(kshell_t *kshell, int argc, char **argv);
int do_deadlock_test(kshell_t *kshell, int argc, char **argv);
static void *deadlock_test_1(int arg1, void *arg2);
static void *deadlock_test_2(int arg1, void *arg2);
/*int do_test_vfstest(kshell_t *kshell, int argc, char **argv);*/
int vm_hello(kshell_t *kshell, int argc, char **argv);
extern void *vfstest_main(int argc, void *argv);

/*int do_thread_cancel(kshell_t *kshell, int argc, char **argv);
static void *thct_master(int arg1, void *arg2);
static void *thct_sub(int arg1, void *arg2);*/
int do_test_prockillall(kshell_t *kshell, int argc, char **argv);
int run_test(kshell_t *kshell, int argc, char **argv);
int waitpid_edge_test(kshell_t *kshell, int argc, char **argv);
kmutex_t m, m1, m2, m3, m4;
/*char pc_array[7];array for producer/consumer test*/
int pc_empty, pc_occupied, pc_nin, pc_nout;
ktqueue_t q_produce, q_consume;
static void *initproc_run_validate(int arg1, void *arg2);
static void *initproc_run_validate2(int arg1, void *arg2);
int do_test_produce_consume(kshell_t *kshell, int argc, char **argv);
static void *produce(int arg1, void *arg2);
static void *consume(int arg1, void *arg2);
static int do_test_random();
static void my_kshell_add_command();
static context_t bootstrap_context;
proc_t *init_process;
proc_t *testprocs;
kthread_t *testthreads;

extern int globalSeed;
extern void mySrand(int givenSeed);
extern void vnodeHelper();
/*---------------------vfs test started---------------------------*/


int vfs_test1(kshell_t *kshell, int argc, char **argv);
int vfs_test2(kshell_t *kshell, int argc, char **argv);
int vfs_test3(kshell_t *kshell, int argc, char **argv);
int vfs_test4(kshell_t *kshell, int argc, char **argv);
int test_special_cleanpage(vnode_t *file, off_t offset, void *pagebuf);
int test_special_dirtypage(vnode_t *file, off_t offset);
int test_special_fillpage(vnode_t *file, off_t offset, void *pagebuf);
int test_fork(kshell_t *kshell, int argc, char **argv);
void *fork_hello_world(int arg1, void *arg2);
int special_device_test(kshell_t *kshell, int argc, char **argv);

int TestMode=0;

int special_device_test(kshell_t *kshell, int argc, char **argv)
{
	do_mkdir("/mytest");
	do_mknod("/mytest/byte_vnode", S_IFCHR, MEM_ZERO_DEVID);

	uint32_t testPageNum=32855;
	vnode_t* specialVnode;
	open_namev("/mytest/byte_vnode",0,&specialVnode,NULL);

	vmmap_t* special_vmmap=vmmap_create();

    int perms = 0;
	perms |= PROT_READ;
	perms |= PROT_WRITE;
	perms |= PROT_EXEC;

	vmarea_t *vmarea;

	dbg(DBG_VM, "Test special mmap\n");
	vmmap_map(special_vmmap,specialVnode,testPageNum,1,perms,MAP_ANON|MAP_SHARED,0,0, &vmarea);

	pframe_t *pfr;
	pframe_get(vmarea->vma_obj, testPageNum,  &pfr);
	specialVnode->vn_mmobj=*(vmarea->vma_obj);
	dbg(DBG_VM, "Test special fillpage");
	specialVnode->vn_cdev->cd_ops->fillpage=test_special_fillpage;
	specialVnode->vn_ops->fillpage(specialVnode,0,PN_TO_ADDR(testPageNum));

	dbg(DBG_VM, "Test special dirtypage\n");
	specialVnode->vn_cdev->cd_ops->dirtypage=test_special_dirtypage;
	specialVnode->vn_ops->dirtypage(specialVnode,0);

	dbg(DBG_VM, "Test special cleanpage\n");
	specialVnode->vn_cdev->cd_ops->cleanpage=test_special_cleanpage;
	specialVnode->vn_ops->cleanpage(specialVnode,0,PN_TO_ADDR(testPageNum));
	return 0;
}
/**
 * This is the first real C function ever called. It performs a lot of
 * hardware-specific initialization, then creates a pseudo-context to
 * execute the bootstrap function in.
 */
void kmain() {
	GDB_CALL_HOOK(boot);

	dbg_init();
	dbgq(DBG_CORE, "Kernel binary:\n");
	dbgq(DBG_CORE, "  text: 0x%p-0x%p\n", &kernel_start_text, &kernel_end_text);
	dbgq(DBG_CORE, "  data: 0x%p-0x%p\n", &kernel_start_data, &kernel_end_data);
	dbgq(DBG_CORE, "  bss:  0x%p-0x%p\n", &kernel_start_bss, &kernel_end_bss);

	page_init();

	pt_init();
	slab_init();
	pframe_init();

	acpi_init();
	apic_init();
	intr_init();

	gdt_init();

	/* initialize slab allocators */
#ifdef __VM__
	anon_init();
	shadow_init();
#endif
	vmmap_init();
	proc_init();
	kthread_init();

#ifdef __DRIVERS__
	bytedev_init();
	blockdev_init();
#endif

	void *bstack = page_alloc();
	pagedir_t *bpdir = pt_get();
	KASSERT(NULL != bstack && "Ran out of memory while booting.");
	context_setup(&bootstrap_context, bootstrap, 0, NULL, bstack, PAGE_SIZE,
			bpdir);
	context_make_active(&bootstrap_context);

	panic("\nReturned to kmain()!!!\n");
}

/**
 * This function is called from kmain, however it is not running in a
 * thread context yet. It should create the idle process which will
 * start executing idleproc_run() in a real thread context.  To start
 * executing in the new process's context call context_make_active(),
 * passing in the appropriate context. This function should _NOT_
 * return.
 *
 * Note: Don't forget to set curproc and curthr appropriately.
 *
 * @param arg1 the first argument (unused)
 * @param arg2 the second argument (unused)
 */
static void *
bootstrap(int arg1, void *arg2) {
	/* necessary to finalize page table information */
	pt_template_init();
	proc_t *idle_process;
	kthread_t *idle_thread;
	idle_process = proc_create("idleprocess");
	curproc = idle_process;

	KASSERT(NULL != curproc);
	KASSERT(PID_IDLE == curproc->p_pid);

	idle_thread = kthread_create(idle_process, idleproc_run, arg1, arg2);
	KASSERT(NULL != curproc);
	dbg(DBG_INIT,"(GRADING 1.a) In bootstrap(): The idle process has been created successfully\n");
	KASSERT(PID_IDLE == curproc->p_pid);
	dbg(DBG_INIT,"(GRADING 1.a) In bootstrap(): The process has been created is the idle process\n");
	curthr = idle_thread;
	curthr->kt_state = KT_RUN;

	KASSERT(NULL != curthr);/* make sure that the thread for the "idle" process has been created successfully */

	context_make_active(&(idle_thread->kt_ctx));
	KASSERT(NULL != curthr);
	dbg(DBG_INIT,"(GRADING 1.a) In bootstrap(): The thread for the idle process has been created successfully\n");
	/*NOT_YET_IMPLEMENTED("PROCS: bootstrap");*/
	panic("weenix returned to bootstrap()!!! BAD!!!\n");
	return NULL;
}

/**
 * Once we're inside of idleproc_run(), we are executing in the context of the
 * first process-- a real context, so we can finally begin running
 * meaningful code.
 A
 *
 * This is the body of process 0. It should initialize all that we didn't
 * already initialize in kmain(), launch the init process (initproc_run),
 * wait for the init process to exit, then halt the machine.
 *
 * @param arg1 the first argument (unused)
 * @param arg2 the second argument (unused)
 */
static void *
idleproc_run(int arg1, void *arg2) {

	int status;
	pid_t child;

	/* create init proc */
	kthread_t *initthr = initproc_create();

	init_call_all();
	GDB_CALL_HOOK(initialized);

	/* Create other kernel threads (in order) */

#ifdef __VFS__
	/* Once you have VFS remember to set the current working directory
	 * of the idle and init processes */

	curproc->p_cwd = vfs_root_vn;
	initthr->kt_proc->p_cwd = vfs_root_vn;
	vref(curproc->p_cwd);
	vref(curproc->p_cwd);
	/* Here you need to make the null, zero, and tty devices using mknod */
	do_mkdir("/dev");
	do_mknod("/dev/null", S_IFCHR, MEM_NULL_DEVID);
	do_mknod("/dev/zero", S_IFCHR,MEM_ZERO_DEVID);
	do_mknod("/dev/tty0", S_IFCHR, MKDEVID(2, 0));
	/* You can't do this until you have VFS, check the include/drivers/dev.h
	 * file for macros with the device ID's you will need to pass to mknod */
#endif

	/* Finally, enable interrupts (we want to make sure interrupts
	 * are enabled AFTER all drivers are initialized) */
	intr_enable();

	/* Run initproc */
	sched_make_runnable(initthr);
	/* Now wait for it */
	child = do_waitpid(-1, 0, &status);
	vnodeHelper();
	KASSERT(PID_INIT == child);

#ifdef __MTP__
	kthread_reapd_shutdown();
#endif

#ifdef __VFS__
	/* Shutdown the vfs: */
	dbg_print("weenix: vfs shutdown...\n");
	vput(curproc->p_cwd);
	if (vfs_shutdown())
	panic("vfs shutdown FAILED!!\n");

#endif

	/* Shutdown the pframe system */
#ifdef __S5FS__
	pframe_shutdown();
#endif

	dbg_print("\nweenix: halted cleanly!\n");
	GDB_CALL_HOOK(shutdown);
	hard_shutdown();
	return NULL;
}

/**
 * This function, called by the idle process (within 'idleproc_run'), creates the
 * process commonly refered to as the "init" process, which should have PID 1.
 *
 * The init process should contain a thread which begins execution in
 * initproc_run().
 *
 * @return a pointer to a newly created thread which will execute
 * initproc_run when it begins executing
 */
static kthread_t *
initproc_create(void) {
	/*proc_t *init_process;*/
	kthread_t *init_thread;
	init_process = proc_create("initprocess");
	KASSERT(NULL != init_process);
	dbg(DBG_INIT,"(GRADING 1.b) In initproc_create(): The init process has been created successfully\n");
	KASSERT(PID_INIT == init_process->p_pid);/*initial process pID*/
	dbg(DBG_INIT,"(GRADING 1.b) In initproc_create(): The process created is init process\n");
	init_thread = kthread_create(init_process, initproc_run, 0, NULL);
	KASSERT(init_thread != NULL);
	dbg(DBG_INIT,"(GRADING 1.b) In initproc_create(): The thread for the init process has been created successfully\n");
	/*NOT_YET_IMPLEMENTED("PROCS: initproc_create");*/
	return init_thread;
}

/**
 * The init thread's function changes depending on how far along your Weenix is
 * developed. Before VM/FI, you'll probably just want to have this run whatever
 * tests you've written (possibly in a new process). After VM/FI, you'll just
 * exec "/bin/init".
 *
 * Both arguments are unused.
 *
 * @param arg1 the first argument (unused)
 * @param arg2 the second argument (unused)
 */
static void *
initproc_run(int arg1, void *arg2) {

	/*	 pagefault error here ??*/

	proc_t *proc_shell;
	kthread_t *thr_shell;
	int pid;
	int status;
	my_kshell_add_command();
	char *argv[] = { NULL };
    char *envp[] = { NULL };
    const char* dir;
/*    kernel_execve("/usr/bin/memtest", argv, envp);*/
	switch(VMTEST){
	case 1:
		dir="/usr/bin/segfault";
    	break;
	case 2:
		dir="/usr/bin/hello";
    	break;
	case 3:
		dir="/usr/bin/args";
		break;
	case 6:
		dir="/usr/bin/kshell";
		break;
	case 5:
		dir="/usr/bin/spin";
		break;
	case 4:
		dir="/sbin/halt";
		break;
	}

	kernel_execve(dir, argv, envp);
	while ((pid = do_waitpid(-1, 0, &status)) > 0) {
		dbg_print("process PID %d returned to initproc_run\n", pid);
	}

	/*do_test_runkill();

	 NOT_YET_IMPLEMENTED("PROCS: initproc_run");
	 do_test_produce_consume();
	 do_deadlock_test();*/
	return NULL;
}

static void
my_kshell_add_command()
{
	kshell_add_command("lifecycle", do_test_runkill,
		"Grading D.1/D.2: create/terminate a bunch of processes and threads");
kshell_add_command("randomrunkill", do_test_random,
		"Grading D.1/D.2: create/terminate a bunch of processes and threads in randomly order");
kshell_add_command("cpTest", do_test_produce_consume,
		"Grading D.4: consumer/producer test");
kshell_add_command("deadlock", do_deadlock_test,
		"Grading D.3: two threads hanging in deadlock");
kshell_add_command("pkillalltest", do_test_prockillall, "kill all procs");
kshell_add_command("runtest", run_test, "run test");
kshell_add_command("waitpidtest", waitpid_edge_test, "waitpid test");
/*kshell_add_command("vfstest",do_test_vfstest,"vfs test");*/

kshell_add_command("vfstest1",vfs_test1,"vfs test: create read write");
kshell_add_command("vfstest2",vfs_test2,"vfs test: failure circumstance");
kshell_add_command("vfstest3",vfs_test3,"vfs test: dup and dup2");
kshell_add_command("vfstest4",vfs_test4,"vfs test: rename");

kshell_add_command("vm_hello",vm_hello,"vm test: hello");
kshell_add_command("special",special_device_test,"vm test: special");
kshell_add_command("fork",test_fork,"vm test: fork");

	}

static void*
kshell_invoke(int arg1, void *arg2) {
	my_kshell_add_command();
	int err = 0;
	kshell_t *ksh = kshell_create(0);
	KASSERT(ksh && "did not create a kernel shell as expected");
	while ((err = kshell_execute_next(ksh)) > 0)
		;
	KASSERT(err == 0 && "kernel shell exited with an error\n");
	kshell_destroy(ksh);
	return NULL;
}
ktqueue_t run_test_q;
int test1_f = 0, test2_f = 0, test3_f = 0;
static void *run_test_1(int arg1, void *arg2);
static void *run_test_2(int arg1, void *arg2);
static void *run_test_3(int arg1, void *arg2);
int run_test(kshell_t *kshell, int argc, char **argv){
	KASSERT(kshell != NULL);
	int status = 0;
	char buffer[1024];
	dbg(DBG_TEST,"run_test initiated, create 3 processes and wait for them.\n");

	sched_queue_init(&run_test_q);
	int pid_exited = 0;

		testprocs = proc_create("run_test_proc_1");
		dbg(DBG_PROC,"PID%d created.\n", testprocs->p_pid);
		kthread_t *run_testthreads1 = kthread_create(testprocs,
				run_test_1, testprocs->p_pid, NULL);
		sched_make_runnable(run_testthreads1);
		testprocs = proc_create("run_test_proc_2");
		dbg(DBG_PROC,"PID%d created.\n", testprocs->p_pid);
		kthread_t *run_testthreads2 = kthread_create(testprocs,
				run_test_2, testprocs->p_pid, NULL);
		sched_make_runnable(run_testthreads2);
		testprocs = proc_create("run_test_proc_3");
		dbg(DBG_PROC,"PID%d created.\n", testprocs->p_pid);
		kthread_t *run_testthreads3 = kthread_create(testprocs,
				run_test_3, testprocs->p_pid, NULL);
		sched_make_runnable(run_testthreads3);
		while ((pid_exited = do_waitpid(-1, 0, &status)) > 0) {
			dbg(DBG_PROC,"PID%d exited with status: %d.\n", pid_exited, status);
		}
		dbg_print("run_test terminated:\n");
		proc_list_info(NULL, buffer, 1024);
		dbg_print("%s", buffer);

		return 0;
}
static void *
run_test_1(int arg1, void *arg2){
	dbg(DBG_TEST,"run_test_1 initiated\n");

	int num = 3, count;
	for(count = 0; count < 3; count++){
		dbg(DBG_TEST,"%d time run_test_1\n",count);
		sched_wakeup_on(&run_test_q);
		if(count != 2){
			sched_sleep_on(&run_test_q);
		}
	}
	return 0;
}
static void *
run_test_2(int arg1, void *arg2){
	dbg(DBG_TEST,"run_test_2 initiated\n");

	int num = 3, count;
	for(count = 0; count < 3; count++){
		dbg(DBG_TEST,"%d time run_test_2\n",count);
		sched_wakeup_on(&run_test_q);
		if(count != 2){
			sched_sleep_on(&run_test_q);
		}
	}
	return 0;
}
static void *
run_test_3(int arg1, void *arg2){
	dbg(DBG_TEST,"run_test_3 initiated\n");

	int num = 3, count;
	for(count = 0; count < 3; count++){
		dbg(DBG_TEST,"%d time run_test_3\n",count);
		sched_wakeup_on(&run_test_q);
		if(count != 2){
			sched_sleep_on(&run_test_q);
		}

	}
	return 0;
}
void creat_test_thr(int testnum){
	int test_num = testnum;
	int count = 0;
	char buffer[1024];
	proc_list_info(NULL, buffer, 1024);
	dbg_print("%s", buffer);
	for (count = 0; count < test_num; count++) {
		char name[40];
		sprintf(name, "initproc_test_proc_%d", count);
		testprocs = proc_create(name);
		dbg(DBG_PROC,"PID%d created.\n", testprocs->p_pid);
		testthreads = kthread_create(testprocs,
				initproc_run_validate, testprocs->p_pid, NULL);
		sched_make_runnable(testthreads);
	}
	dbg_print("process created:\n");
	proc_list_info(NULL, buffer, 1024);
	dbg_print("%s", buffer);
}
int do_test_runkill(kshell_t *kshell, int argc, char **argv) {
	KASSERT(kshell != NULL);
	int status = 0;
	int pid_exited;
	dbg(DBG_TEST,"initproc_run initiated\n");

	dbg(DBG_TEST,"(GRADING D.1)create process and threads\n");
	dbg_print("initial state:\n");

	creat_test_thr(5);

	while ((pid_exited = do_waitpid(-1, 0, &status)) > 0) {
		dbg(DBG_PROC,"PID%d exited with status: %d.\n", pid_exited, status);
	}
	dbg_print("after process terminated:\n");
	char buffer[1024];
	proc_list_info(NULL, buffer, 1024);
	dbg_print("%s", buffer);

	return 0;
}
int waitpid_edge_test(kshell_t *kshell, int argc, char **argv){
	KASSERT(kshell != NULL);
	int status = 0;
	int pid_exited;
	dbg(DBG_TEST,"waitpid_edge_test initiated\n");
	creat_test_thr(1);
	dbg(DBG_TEST,"wait for proc (pid %d) with arg %d, 0, &status\n",testprocs->p_pid,testprocs->p_pid);
	pid_exited = do_waitpid(testprocs->p_pid, 0, &status);
	dbg(DBG_PROC,"PID%d exited with status: %d.\n", pid_exited, status);
	creat_test_thr(2);
	dbg(DBG_TEST,"wait for procs with arg -1, 0, &status\n");
	while ((pid_exited = do_waitpid(-1, 0, &status)) > 0) {
		dbg(DBG_PROC,"PID%d exited with status: %d.\n", pid_exited, status);
	}
	dbg(DBG_TEST,"wait for procs with arg pid other than -1&>0,option other than 0,this should halt!\n");
	creat_test_thr(1);
	pid_exited = do_waitpid(-2, 1, &status);
	dbg(DBG_TEST,"didn't halt, something wrong!\n");
	return 0;
}


/* not finished */
int do_test_produce_consume(kshell_t *kshell, int argc, char **argv) {
	dbg(DBG_TEST,"This is the producer~!.\n");
	pc_empty = 6, pc_occupied = 0, pc_nin = 0, pc_nout = 0;/*initialize global variables*/
	/*memset (&pc_array,'-',sizeof(pc_array));*/
	kmutex_init(&m);
	sched_queue_init(&q_produce);/*queue类似于conditional variable? 这是唯一能实现cond_wait和cond_signal的方法了吧*/
	sched_queue_init(&q_consume);
	proc_t *proc_producer = proc_create("producer");
	proc_t *proc_consumer = proc_create("consumer");
	kthread_t *thr_producer = kthread_create(proc_producer, produce, 0, NULL);
	kthread_t *thr_consumer = kthread_create(proc_consumer, consume, 0, NULL);

	sched_make_runnable(thr_producer);
	sched_make_runnable(thr_consumer);
	dbg(DBG_TEST,"This is the producer2~!.\n");
	int status;
	do_waitpid(proc_producer->p_pid, 0, &status);
	dbg(DBG_TEST,"Producer exited!\n");
	do_waitpid(proc_consumer->p_pid, 0, &status);
	dbg(DBG_TEST,"This is the producer3~!.\n");
	return 0;
}

static void *
produce(int arg1, void *arg2) {
	dbg(DBG_THR ,"produce thread started.\n");
	/*	kmutex_lock(&m);*/
	while (1) {
		kmutex_lock(&m);
		if (pc_empty > 0) {
			pc_empty--;
			pc_nin++;
			pc_occupied++;
			dbg(DBG_THR ,"Produced empty=%d, nin=%d.\n",pc_empty,pc_nin);
			if (pc_nin == 6) {
				/*	kthread_exit(0);*/
				kmutex_unlock(&m);
				sched_wakeup_on(&q_consume);
				return 0;
			}
		}
		kmutex_unlock(&m);
		sched_wakeup_on(&q_consume);
		sched_sleep_on(&q_produce);
	}
	return NULL;
}

static void *
consume(int arg1, void *arg2) {
	dbg(DBG_THR ,"consume thread starte.\n");
	/*	kmutex_lock(&m);*/
	while (1) {
		kmutex_lock(&m);
		if (pc_occupied > 0) {
			pc_occupied--;
			pc_nout++;
			pc_empty++;
			dbg(DBG_THR ,"consumed empty=%d, nout=%d.\n",pc_empty,pc_nout);
			if (pc_nout == 6) {
				/*	kthread_exit(0);*/
				kmutex_unlock(&m);
				sched_wakeup_on(&q_produce);
				return 0;
			}

		}
		kmutex_unlock(&m);
		sched_wakeup_on(&q_produce);
		sched_sleep_on(&q_consume);
	}
	return NULL;
}

static void *
initproc_run_validate(int arg1, void *arg2) {
	/*char buffer [60];
	 sprintf(buffer,"test thread %d created and ended.\n",arg1);*/
	dbg(DBG_THR,"thread created in PID %d and terminate.\n",arg1);
	/*kthread_exit(0);*/
	return 0;
}

int do_deadlock_test(kshell_t *kshell, int argc, char **argv) {
	dbg(DBG_TEST,"deadlock test start.\n");
	kmutex_init(&m1);
	kmutex_init(&m2);
	proc_t * proc_dl1, *proc_dl2;
	kthread_t *kthread_dl1, *kthread_dl2;

	proc_dl1 = proc_create("deadlocktest_thr1");

	proc_dl2 = proc_create("deadlocktest_thr2");
	kthread_dl2 = kthread_create(proc_dl2, deadlock_test_2, 0, NULL);
	kthread_dl1 = kthread_create(proc_dl1, deadlock_test_1, 0, kthread_dl2);
	sched_make_runnable(kthread_dl1);
	sched_make_runnable(kthread_dl2);
	do_waitpid(proc_dl1->p_pid, 0, 0);
	do_waitpid(proc_dl2->p_pid, 0, 0);
	return 0;
}

static void *
deadlock_test_1(int arg1, void *kthread_dl2) {
	dbg(DBG_THR,"thread 1 try lock mutex 1\n");
	kmutex_lock(&m1);
	dbg(DBG_THR,"thread 1 lock mutex 1\n");
	sched_make_runnable(curthr);
	sched_switch();
	dbg(DBG_THR,"thread 1 try lock mutex 2\n");
	while (m2.km_holder != (kthread_t *) kthread_dl2) {
		sched_make_runnable(curthr);
		sched_switch();
	}/*make sure deadlock happen*/
	kmutex_lock(&m2);
	dbg(DBG_THR,"thread 1 lock mutex 2\n");
	sched_make_runnable(curthr);
	sched_switch();
	kmutex_unlock(&m2);
	dbg(DBG_THR,"thread 1 unlock mutex 2\n");
	kmutex_unlock(&m1);
	dbg(DBG_THR,"thread 1 unlock mutex 1\n");
	dbg(DBG_THR,"thread 1 exiting\n");

	return NULL;
}

static void *
deadlock_test_2(int arg1, void *arg2) {
	dbg(DBG_THR,"thread 2 try lock mutex 2\n");
	kmutex_lock(&m2);
	dbg(DBG_THR,"thread 2 lock mutex 2\n");
	sched_make_runnable(curthr);
	sched_switch();
	dbg(DBG_THR,"thread 2 try lock mutex 1\n");
	kmutex_lock(&m1);
	dbg(DBG_THR,"thread 2 lock mutex 1\n");
	sched_make_runnable(curthr);
	sched_switch();
	kmutex_unlock(&m1);
	dbg(DBG_THR,"thread 2 unlock mutex 1\n");
	kmutex_unlock(&m2);
	dbg(DBG_THR,"thread 2 unlock mutex 2\n");
	dbg(DBG_THR,"thread 2 exiting\n");
	return NULL;
}

/*int do_thread_cancel(kshell_t *kshell, int argc, char **argv) {
	dbg(DBG_TEST,"thread cancel start.\n");
	proc_t * proc_dl1, *proc_dl2;
	kthread_t *kthread_dl1, *kthread_dl2;
	proc_dl1 = proc_create("master_process");
	proc_dl2 = proc_create("sub_process");
	kthread_dl2 = kthread_create(proc_dl2, thct_sub, 0, NULL);
	kthread_dl1 = kthread_create(proc_dl1, thct_master, 0, kthread_dl2);
	sched_make_runnable(kthread_dl2);
	do_waitpid(proc_dl1->p_pid, 0, 0);
	do_waitpid(proc_dl2->p_pid, 0, 0);
	return 0;
}

static void *
thct_master(int arg1, void *kthread) {
	dbg(DBG_THR,"master thread started\n");
	dbg(DBG_THR,"master thread trying to cancel sub thread\n");
	kthread_cancel((kthread_t *) kthread, NULL);
	dbg(DBG_THR,"sub thread should now be cancelled\n");
	return NULL;
}

static void *
thct_sub(int arg1, void *arg2) {
	dbg(DBG_THR,"sub thread started\n");
	dbg(DBG_THR,"sub thread exiting\n");
	return NULL;
}*/

static void *
initproc_run_validate2(int arg1, void *arg2) {
	/*char buffer [60];
	 sprintf(buffer,"test thread %d created and ended.\n",arg1);*/
	dbg(DBG_THR,"test thread %d created.\n",arg1);
	while (1) {

	}
	/*kthread_exit(0);*/
	return 0;
}
int do_test_prockillall(kshell_t *kshell, int argc, char **argv) {
	KASSERT(kshell != NULL);
	int status = 0;
	dbg(DBG_CORE,"initproc_run initiated\n");

	dbg(DBG_CORE,"test proc_kill_all");
	int test_num = 5;
	int count = 0;
	int pid_exited=0;
	for (count = 0; count < test_num; count++) {
		char name[40];
		sprintf(name, "initproc_test_proc_%d", count);
		testprocs = proc_create(name);
		dbg(DBG_PROC,"PID%d created.\n", testprocs->p_pid);
		kthread_t *testthreads = kthread_create(testprocs,
				initproc_run_validate, testprocs->p_pid, NULL);
		sched_make_runnable(testthreads);
	}
	proc_kill_all();
/*	while ((pid_exited = do_waitpid(-1, 0, &status)) > 0) {
		dbg(DBG_PROC,"PID%d exited with status: %d.\n", pid_exited, status);
	}*/
	return 0;
}

int  do_test_random(kshell_t *kshell, int argc, char **argv)
{
	dbg(DBG_TEST,"Enter to random exit!\n");
	mySrand(100);
	KASSERT(kshell != NULL);
	return do_test_runkill(kshell,argc,argv);
	}
/*-----------------------------------vfs------------------------------------------------------------------*/
int vfs_test1(kshell_t *kshell, int argc, char **argv){
	dbg(DBG_TEST,"enter vfs_test1,create a file named test1 in read write mode\n");
	int fd, byteswrite,bytesread;
	char *buf = "write to test1 in function vfs_test1!";
	char bufr[100];
	fd = do_open("test1", O_CREAT|O_RDWR);
	if(fd >= 0 && fd <= NFILES){
		dbg(DBG_TEST,"file: test1 create successfully, return file descriptor %d\n",fd);
	}
	else{
		if(fd == -EMFILE){
			dbg(DBG_TEST,"create failed, not enough file descriptor left\n");
			return 0;
		}
		if(fd == ENOMEM){
			dbg(DBG_TEST,"create failed, not enough memory left\n");
			return 0;
		}
	}
	byteswrite = do_write(fd,buf,strlen(buf));
	dbg(DBG_TEST,"write (%s) to test1 finished, number of bytes write: %d\n",buf,byteswrite);
	do_close(fd);
	dbg(DBG_TEST,"file test1 closed\n");
	fd = do_open("test1",O_RDONLY);
	if(fd >= 0 && fd <= NFILES){
		dbg(DBG_TEST,"file: test1 opened successfully, return file descriptor %d\n",fd);
	}
	else{
		if(fd == -EMFILE){
			dbg(DBG_TEST,"open failed, not enough file descriptor left\n");
			return 0;
		}
	}
	bytesread = do_read(fd,bufr,strlen(buf));
	dbg(DBG_TEST,"read from test1 finished, number of bytes read:%d\n",bytesread);
	dbg(DBG_TEST,"the content read is (%s)\n",bufr);
	do_close(fd);
	struct stat s;
	do_mkdir("testdir");
	do_rmdir("testdir");
	do_stat("test1",&s);
	dbg(DBG_TEST,"file test1 closed, test finish\n");
	return 0;
}
int vfs_test2(kshell_t *kshell, int argc, char **argv){
	dbg(DBG_TEST,"enter vfs_test2,open file test1 and try to read and write\n");
	int fd, byteswrite, bytesread,count,fd2[100],num;
	char *buf = "write to file test1 in function vfs_test2!";
	char bufr[100];
	dbg(DBG_TEST,"open file read only and try to wirte\n");
	fd = do_open("test1",O_RDONLY|O_CREAT);
	if(fd >= 0 && fd <= NFILES){
		dbg(DBG_TEST,"file: test1 opened successfully, return file descriptor %d\n",fd);
	}
	else{
		if(fd == -EMFILE){
			dbg(DBG_TEST,"open failed, not enough file descriptor left\n");
			return 0;
		}
	}
	byteswrite = do_write(fd,buf,strlen(buf));
	if(byteswrite == -EBADF){
		dbg(DBG_TEST,"write failed, file test1 is not open for write\n");
	}
	do_close(fd);
	dbg(DBG_TEST,"open file write only and try to read\n");
	fd = do_open("test1",O_WRONLY);
	if(fd >= 0 && fd <= NFILES){
		dbg(DBG_TEST,"file: test1 opened successfully, return file descriptor %d\n",fd);
	}
	else{
		if(fd == -EMFILE){
			dbg(DBG_TEST,"open failed, not enough file descriptor left\n");
			return 0;
		}
	}
	bytesread = do_read(fd,bufr,100);
	if(bytesread == -EBADF){
		dbg(DBG_TEST,"read failed, file test1 is not open for read\n");
	}
	do_close(fd);
	
	dbg(DBG_TEST,"open more than NFILES file\n");
	for(count = 0; count <= 100; count++){
		fd2[count] = do_open("test1",O_RDONLY);
		if(fd2[count] == -EMFILE){
			dbg(DBG_TEST,"open failed, The process already has the maximum number of files open\n");
			break;
		}
		dbg(DBG_TEST,"open successful, fd: %d\n",fd2[count]);
		num = count;
	}
	for(count = 0; count <=num; count++){
		do_close(fd2[count]);
	}
	dbg(DBG_TEST,"vfs_test2 finish\n");
	return 0;
}
int vfs_test3(kshell_t *kshell, int argc, char **argv){
	dbg(DBG_TEST,"enter vfs_test3\n");
	int fd,fd2,dup_fd,dup2_fd[3], nfd[3],count;
	fd = do_open("test1",O_RDONLY);
	fd2 = do_open("test2",O_RDONLY|O_CREAT);
	if(fd >= 0 && fd <= NFILES){
		dbg(DBG_TEST,"file: test1 opened successfully, return file descriptor %d\n",fd);
	}
	else{
		if(fd == -EMFILE){
			dbg(DBG_TEST,"open failed, not enough file descriptor left\n");
			return 0;
		}
	}
	dbg(DBG_TEST,"file: test1,fd %d,open mode %d, refcount %d\n",fd,curproc->p_files[fd]->f_mode,curproc->p_files[fd]->f_refcount);
	dbg(DBG_TEST,"dup test begin\n");
	dup_fd = do_dup(fd);
	if(dup_fd == -EMFILE){
		dbg(DBG_TEST,"The process already has the maximum number of file descriptors open\n");
		return 0;
	}
	else{
		dbg(DBG_TEST,"dup test1 success, newfd %d, open mode %d, refcount %d\n",dup_fd,curproc->p_files[dup_fd]->f_mode,curproc->p_files[dup_fd]->f_refcount);
	}
	dbg(DBG_TEST,"dup2 test begin\n");
	nfd[0] = 30;
	nfd[1] = fd;
	nfd[2] = fd2;
	for(count = 0; count<3; count++){
		dup2_fd[count] = do_dup2(fd,nfd[count]);
		if(dup2_fd[count] != -EBADF)
			dbg(DBG_TEST,"dup2 test1 success, newfd %d, open mode %d, refcount %d\n",dup2_fd[count],curproc->p_files[dup2_fd[count]]->f_mode,curproc->p_files[dup2_fd[count]]->f_refcount);
	}
	do_close(fd);
	do_close(fd2);
	do_close(dup_fd);
	for(count = 0; count < 3; count++){
		do_close(dup2_fd[count]);
	}
	return 0;
}
int vfs_test4(kshell_t *kshell, int argc, char **argv){
	dbg(DBG_TEST,"rename test.\n");
	do_mkdir("renametest");
	do_chdir("renametest");

	do_open("before_rename.txt",O_CREAT);
	do_rename("before_rename.txt","rename_successful.txt");
	return 0;
}

int vm_hello(kshell_t *kshell, int argc, char **argv)
{
    	char *envp[] = { NULL };
    	kernel_execve("/usr/bin/hello", argv, envp);
    	return 0;
	}



int test_special_fillpage(vnode_t *file, off_t offset, void *pagebuf)
{
	pframe_t* pfr;
	pframe_get(&(file->vn_mmobj), ADDR_TO_PN(pagebuf), &pfr);
    pframe_set_busy(pfr);
	return file->vn_mmobj.mmo_ops->fillpage(&(file->vn_mmobj), pfr);

	}

int test_special_dirtypage(vnode_t *file, off_t offset)
{
	pframe_t* pfr=NULL;
	return file->vn_mmobj.mmo_ops->dirtypage(&(file->vn_mmobj), pfr);
	}


int test_special_cleanpage(vnode_t *file, off_t offset, void *pagebuf)
{
	pframe_t* pfr;
	pframe_get(&(file->vn_mmobj), ADDR_TO_PN(pagebuf), &pfr);
    pframe_set_busy(pfr);
	return file->vn_mmobj.mmo_ops->cleanpage(&(file->vn_mmobj), pfr);
}

void *fork_hello_world(int arg1, void *arg2)
{
	int fork_pid;
	regs_t* reg=(regs_t*)0xc1eb0fbc;
	fork_pid = do_fork(reg);
	kshell_t *ksh;
    ksh = kshell_create(0);
    const char* intro;

    if(fork_pid!=0)
    {
    	intro = "I am parent process to fork.";
    }
    else
    {
    	intro = "I am child process being forked";
    }
	if(0)
	{
		kprintf(ksh, "Hello World!%s\n",intro);
	}

	return NULL;
	}

int test_fork(kshell_t *kshell, int argc, char **argv)
{
	TestMode=1;
	int pid_exited,status;
	status=0;

	proc_t* fork_test_process = proc_create("fork_hello_world");
	kthread_t* fork_test_thread = kthread_create(fork_test_process, fork_hello_world, 0, NULL);
	sched_make_runnable(fork_test_thread);
	proc_t* test_process = proc_create("fork_hello_world");
	kthread_t* test_thread = kthread_create(test_process, initproc_run_validate, 0, NULL);
	sched_make_runnable(test_thread);

	while ((pid_exited = do_waitpid(-1, 0, &status)) < 0) {
		dbg(DBG_PROC,"test fork error\n");
	}

	TestMode=0;
	return 0;
}
/*int do_test_vfstest(kshell_t *kshell, int argc, char **argv)
{
	int status = 0;
	proc_t *proc_vfstest = proc_create("vfstest");
	kthread_t *thr_vfstest = kthread_create(proc_vfstest, vfstest_main, 1, NULL);
	sched_make_runnable(thr_vfstest);
	do_waitpid(proc_vfstest->p_pid, 0, &status);
	return 0;
}*/
/**
 * Clears all interrupts and halts, meaning that we will never run
 * again.
 */
static void hard_shutdown() {
#ifdef __DRIVERS__
	vt_print_shutdown();
#endif
	__asm__ volatile("cli; hlt");
}
